#include <stdarg.h>

#include "lib/pvmlog.h"
#include "lib/pvmstrings.h"
#include "lib/pvmsystem.h"

static pvm_bool isLogggingInitialized = false;
volatile static pvm_bool loggingEnabled = false;
static PVMLogOutFunction logFunction;
static pvm_int32	pvmLogEventMask;
static pvm_int32	pvmLogComponentMask;
static pvm_bool		pvmLogPrintLocation;

const pvm_char* PVMLogGetComponentName(enum PVMLOG_COMPONENT component);
const pvm_char* PVMLogGetLogTypeString(enum PVMLOG_EVENT_TYPE logType);

void PVMCALL PVMLogDefaultOutFunction(const pvm_char* logString)
{
	PVMSysLogOutString(logString);
}

void PVMLogInit()
{
	loggingEnabled = false;
	logFunction = PVMLogDefaultOutFunction;
	pvmLogEventMask = PVMLOG_EVENT_ALL;
	pvmLogComponentMask = PVMLOG_COMPONENT_ALL;
	pvmLogPrintLocation = true;
	isLogggingInitialized = true;
}

PVMAPI void PVMCALL PVMLogEnable(pvm_bool enabled)
{
	loggingEnabled = enabled;
}

PVMAPI pvm_bool PVMCALL PVMLogIsEnabled()
{
	return loggingEnabled;
}

PVMAPI void PVMCALL PVMLogSetEventMask(pvm_int32 mask)
{
	pvmLogEventMask = mask & PVMLOG_EVENT_MASK;
}

PVMAPI pvm_int32 PVMCALL PVMLogGetEventlMask()
{
	return pvmLogEventMask;
}


PVMAPI void PVMCALL PVMLogComponentsEnable(pvm_int32 components, pvm_bool state)
{
	components &= PVMLOG_COMPONENT_MASK;
	if(state)
	{
		pvmLogComponentMask |= components;
	}
	else
	{
		pvmLogComponentMask = pvmLogComponentMask & (~components);
	}
}

PVMAPI pvm_int32 PVMCALL PVMLogGetComponentsState()
{
	return pvmLogComponentMask;
}

PVMAPI void PVMCALL PVMLogLogSourceEnable(pvm_bool enabled)
{
	pvmLogPrintLocation = enabled;
}

PVMAPI pvm_bool PVMCALL PVMLogLogSourceEnableIsEnabled()
{
	return pvmLogPrintLocation;
}

PVMAPI PVMLogOutFunction PVMCALL PVMLogGetDefaultOutFunction()
{
	return PVMLogDefaultOutFunction;
}

PVMAPI PVMLogOutFunction PVMCALL PVMLogGetOutFunction()
{
	return logFunction;
}

PVMAPI pvm_bool PVMCALL PVMLogSetOutFunction(PVMLogOutFunction logOutFunction)
{
	if(logOutFunction==NULL)
		return false;

	logFunction = logOutFunction;
	return true;
}


const pvm_char* PVMLogGetLogTypeString(enum PVMLOG_EVENT_TYPE logType)
{
	switch(logType)
	{
	case PVMLOG_EVENT_ERROR:
		return _PVM_STR("ERROR");
	case PVMLOG_EVENT_WARNING:
		return _PVM_STR("WARNING");
	case PVMLOG_EVENT_INFO:
		return _PVM_STR("INFO");
	default:
		return _PVM_STR("UNKNOWN");
	}
}

void PVMLogWrite(const pvm_char* logSourceFileWithLine, enum PVMLOG_COMPONENT component, enum PVMLOG_EVENT_TYPE logType, const pvm_char* logStringWithFormat, ...)
{
	va_list args;
	pvm_char buffer[4096];
	pvm_char formatString[1024];
	pvm_int formatStringLenth;
	pvm_int maxSprintfLen = 4096;

	if(!isLogggingInitialized)
		return;

	if(logStringWithFormat==NULL)
		return;

	if(!(pvmLogEventMask & logType) || !(pvmLogComponentMask & component))
		return;

	va_start(args, logStringWithFormat);

	pvm_strcpy(formatString, _PVM_STR("[PVM:"));
	pvm_strcat(formatString, PVMLogGetComponentName(component));
	pvm_strcat(formatString, _PVM_STR("] ("));
	pvm_strcat(formatString, PVMLogGetLogTypeString(logType));
	pvm_strcat(formatString, _PVM_STR("): "));

	formatStringLenth = pvm_strlen(formatString);
	pvm_strcat(formatString, logStringWithFormat);

	pvm_char* buffPtr = buffer;

	if(pvmLogPrintLocation && logSourceFileWithLine!= NULL)
	{
		pvm_strcpy(buffer, logSourceFileWithLine);
		pvm_strcat(buffer, _PVM_STR("\r\n"));
		pvm_int strLen = pvm_strlen(buffer);
		buffPtr += strLen;
		maxSprintfLen -= strLen;
		
	}

	pvm_vsprintf(buffPtr, maxSprintfLen, formatString, args);
	logFunction(buffer);
	va_end(args);
}

const pvm_char* PVMLogGetComponentName(enum PVMLOG_COMPONENT component)
{
	switch(component)
	{
	case PVMLOG_COMPONENT_OTHER:
		return _PVM_STR("OTHER");
	case PVMLOG_COMPONENT_MEMORY:
		return _PVM_STR("MEMORY");
	case PVMLOG_COMPONENT_DEVICE:
		return _PVM_STR("DEVICE");
	case PVMLOG_COMPONENT_DRIVER:
		return _PVM_STR("DRIVER");
	case PVMLOG_COMPONENT_MIXING:
		return _PVM_STR("MIXING");
	}
	return _PVM_STR("");
}